<template>
  <div class="p-home">
    <input
      ref="clipboard"
      class="clipboard"
      :value="JSON.stringify(copyData)"
    />
    <div class="toolbar">
      <a-button @click="goback">返回列表</a-button>
      <a-button @click="undo">撤销</a-button>
      <a-button @click="redo" :id="111" title="快捷键 ctrl + z">重做</a-button>
      <a-popconfirm
        title="确定清空内容吗?"
        ok-text="是"
        cancel-text="否"
        @confirm="clear"
      >
        <a-button type="danger">清空</a-button>
      </a-popconfirm>
      <a-upload
        :file-list="fileList"
        :before-upload="beforeUpload"
        @change="uploadJSON"
      >
        <a-button> <a-icon type="upload" />导入JSON</a-button>
      </a-upload>
      <a-button @click="exportJSON">导出JSON</a-button>
      <a-button @click="preview">预览</a-button>
      <a-button @click="save">保存（开发环境）</a-button>
      <a-popconfirm
        title="确定发布吗?"
        ok-text="是"
        cancel-text="否"
        @confirm="deploy"
      >
        <a-button type="danger">发布上线</a-button>
      </a-popconfirm>
    </div>
    <div class="home-container">
      <div class="left">
        <h3 class="title">
          组件列表<span style="color: #333; font-size: 16px">（支持拖拽）</span>
        </h3>
        <draggable
          class="dragArea list-group"
          :list="list"
          :clone="clone"
          :group="{ name: 'people', pull: 'clone', put: false }"
          @change="log"
          @end="dragEnd"
        >
          <transition-group type="transition" name="flip-list">
            <div
              v-for="(element, index) in list"
              :key="element.id"
              class="list-group-item"
              title="可通过鼠标拖拽添加题目到问卷~"
              @click.stop="addSource(element)"
            >
              <component
                :is="element.componentName"
                :element="element"
                :source="source"
                :index="-index"
                :total="source.length"
                @complete="complete"
              />
              <span class="tag">{{ element.name }}</span>
            </div>
          </transition-group>
        </draggable>
      </div>
      <div ref="content" class="center">
        <h3 class="title">
          问卷内容<span
            style="color: #333; font-size: 16px"
          >（支持拖拽和快捷键）</span>
        </h3>
        <draggable
          class="dragArea list-group group-right"
          :list="source"
          group="people"
          :move="onMove"
          filter=".forbid"
          animation="300"
          @change="log"
          @end="dragEnd"
        >
          <transition-group>
            <div
              v-for="(element, index) in source"
              :id="'id_' + index"
              :key="element.id"
              :ref="'customComponentRef' + index"
              :class="{
                'list-group-item': true,
                'item-right': true,
                forbid: element.id === -1,
              }"
              @click="choose(element, index)"
              @contextmenu.prevent="choose(element, index)"
            >
              <component
                :is="element.componentName"
                :element="element"
                :source="source"
                :index="index"
                :total="source.length"
                @complete="complete"
              />
            </div>
          </transition-group>
        </draggable>
      </div>
      <div class="right">
        <span class="title">题目属性区</span>
        <!-- <div>{{ source[activeIndex] }}</div> -->

        <div
          v-if="source[activeIndex] && source[activeIndex].id === -1"
          class=""
        >
          <div v-if="source[activeIndex]" class="flex-row">
            <span>类型：</span>
            <span>{{ source[activeIndex].name }}</span>
          </div>
          <div class="flex-row">
            <span>问卷标题：</span>
            <a-textarea v-model="source[activeIndex].title" />
          </div>
          <div class="flex-row">
            <span>问卷说明：</span>
            <a-textarea v-model="source[0].content" />
          </div>
          <span>问卷分区：</span>
          <div
            v-for="(item, index) in source[0].sections"
            :key="index"
            style="margin-top: 10px"
          >
            <a-icon
              title="删除该内容"
              type="close"
              class="red-icon"
              @click="removeSection(index)"
            />
            <span>分区{{ index + 1 }}：</span><a-input
              v-model="item.title"
              placeholder="分区标题，如第一部分：基本信息"
            /><a-input
              v-model="item.startIndex"
              placeholder="题目开始序号，从1开始"
            /><a-input v-model="item.endIndex" placeholder="题目结束序号" />
          </div>
          <a-button @click="addSection">新增分区</a-button>
        </div>
        <div v-else>
          <div class="flex-row">
            <span>题目序号：</span>
            <span>{{ activeIndex }}</span>
          </div>
          <div v-if="source[activeIndex]" class="flex-row">
            <span>类型：</span>
            <span>{{ source[activeIndex].name }}</span>
          </div>
          <div class="flex-row">
            <span>题目标题：</span>
            <a-textarea v-model="source[activeIndex].title" />
          </div>
          <div v-if="source[activeIndex]" class="flex-row">
            <span>*是否必填：</span>
            <a-radio-group
              v-model="source[activeIndex].requireFlag"
              name="radioGroup"
            >
              <a-radio :value="1"> 是 </a-radio>
              <a-radio :value="0"> 否 </a-radio>
            </a-radio-group>
          </div>
          <div v-if="source[activeIndex]" class="flex-row">
            <span>*是否显示：</span>
            <a-radio-group
              v-model="source[activeIndex].isShow"
              name="radioGroup"
            >
              <a-radio :value="1"> 是 </a-radio>
              <a-radio :value="0"> 否 </a-radio>
            </a-radio-group>
          </div>
        </div>

        <div v-if="source[activeIndex] && !questionId" class="flex-row">
          <span>*显示序号：</span>
          <a-radio-group
            v-model="source[activeIndex].autoIndex"
            name="radioGroup"
            :default-value="1"
            @change="changeAutoIndex"
          >
            <a-radio :value="1"> 是 </a-radio>
            <a-radio :value="0"> 否 </a-radio>
          </a-radio-group>
        </div>
        <div v-if="ifRemarkItem" class="flex-row">
          <span>*设置备注：</span>
          <a-select
            :key="activeIndex"
            mode="multiple"
            :default-value="remarkItems"
            :dropdown-match-select-width="false"
            @change="changeChoiceRemark"
          >
            <a-select-option
              v-for="(item) in source[activeIndex].content"
              :key="item.name"
              :value="item.name"
            >
              {{ item.name }}
            </a-select-option>
          </a-select>
        </div>
        <div
          v-if="
            source[activeIndex] &&
              ['checkbox', 'sort'].includes(source[activeIndex].componentName)
          "
          class=""
        >
          <span>*多选限制：</span>
          <a-input
            v-model="source[activeIndex].minChoices"
            placeholder="最少需选（多选时有效），填0:无限制"
          /><a-input
            v-model="source[activeIndex].maxChoices"
            placeholder="最多可选（多选时有效），填0:无限制"
          />
        </div>
        <br />
        <div
          v-if="
            source[activeIndex] &&
              ['link-table'].includes(source[activeIndex].componentName)
          "
          class=""
        >
          <span>*引用答题别名：</span>
          <p v-for="(item, index) in linkRowList" :key="index">
            <editable-cell
              :text="item"
              @change="onLinkRowChange(index, $event)"
              @remove="onLinkRowRemove(index)"
            />
          </p>
          <p><a-button @click="addLinkRow">添加一行</a-button></p>
        </div>
      </div>
    </div>
    <vue-context-menu
      class="right-menu"
      :target="contextMenuTarget"
      :show="contextMenuVisible"
      @update:show="(show) => (contextMenuVisible = show)"
    >
      <a
        href="javascript:;"
        @click="copy"
      >复制<span style="color: #aaa; margin-left: 20px">Ctrl+C</span></a>
      <a
        href="javascript:;"
        @click="paste"
      >粘贴<span style="color: #aaa; margin-left: 20px">Ctrl+V</span></a>
      <a
        href="javascript:;"
        @click="cut"
      >剪切<span style="color: #aaa; margin-left: 20px">Ctrl+X</span></a>
      <a
        href="javascript:;"
        @click="del"
      >删除<span style="color: #aaa; margin-left: 20px">Delete</span></a>
    </vue-context-menu>
  </div>
</template>

<script>
// @ is an alias to /src
import draggable from 'vuedraggable'
import { component as VueContextMenu } from '@xunlei/vue-context-menu'
import { deepCopy, saveFile, snapshot, readFile, throttle } from '@/utils/common'
import { saveItem, getItem } from '@/utils/store'
import { getById, submit, preview, edit, deployQuestion } from '@/services/lowcodeService'

const saveSource = throttle((source, activeIndex) => {
  saveItem('source', source)
  snapshot.record({ source, activeIndex })
}, 1000)
let idGlobal = 8
const ctrlKey = 17
const vKey = 86
const cKey = 67
const xKey = 88
const zKey = 90
const yKey = 89
const dKey = 46
let isCtrlDown = false
export default {
  name: 'Home',
  components: {
    draggable,
    'vue-context-menu': VueContextMenu,
  },
  data() {
    return {
      fileList: [],
      result: {},
      contextMenuTarget: null,
      contextMenuVisible: false,
      list: [
        {
          content: [{ value: '' }],
          title: '您的姓名？',
          componentName: 'blank',
          name: '填空题',
          id: 3,
        },
        {
          content: [
            { value: 1, id: 1, name: '选项A' },
            { value: 2, id: 2, name: '选项B' },
            { value: 3, id: 3, name: '选项C' },
            { value: 4, id: 4, name: '选项A' },
          ],
          componentName: 'radio',
          name: '单选题',
          title: '您的岗位属于以下哪类？',
          id: 1,
        },
        {
          content: [
            { value: 1, id: 1, name: '选项A' },
            { value: 2, id: 2, name: '选项B' },
            { value: 3, id: 3, name: '选项C' },
            { value: 4, id: 4, name: '选项A' },
          ],
          title: '您认为股权激励应该授予哪些人?',
          componentName: 'checkbox',
          name: '多选题',
          id: 2,
        },
        {
          content: [
            { value: 1, id: 1, name: '选项A' },
            { value: 2, id: 2, name: '选项B' },
            { value: 3, id: 3, name: '选项C' },
          ],
          title: '在股权分配的时候，您认为以下哪个因素重要？请按重要性排序。',
          componentName: 'sort',
          name: '排序题',
          id: 5,
        },
        {
          content: {
            content: [
              [
                'A：',
                '1.我倾向于广泛但关系不紧密的人际圈',
                '2.我周末时喜欢外出，并参加各种聚会',
                '3.我周围的朋友们都说我是很开朗健谈的人',
                '4.我与他人相处时精力充沛',
                '5.我追求知识的广度而不是深度',
              ],
              [
                'B：',
                '1.我倾向于关系紧密但并不广泛的人际圈',
                '2.我周末时倾向于独自进行休闲活动',
                '3.我周围的朋友们都说我是很冷静沉着的人',
                '4.我独处时精力充沛',
                '5.我追求知识的深度而不是广度',
              ],
            ],
            desc: '请根据您的实际情况点击选框您跟AB描述的符合程度，越往左越符合A，越往右越符合B',
            level: [
              'A描述完全符合',
              'A描述非常符合',
              'A描述比较符合',
              'A描述有点符合',
              '无法判断',
              'B描述有点符合',
              'B描述比较符合',
              'B描述非常符合',
              'B描述完全符合',
            ],
          },
          title: '点击开始编辑迫选题标题',
          componentName: 'forced-choice',
          name: '迫选题',
          id: 4,
        },

        {
          content: {
            row: ['行标题1', '行标题2'],
            column: [
              {
                header: '列标题1',
                columnId: '1',
                choices: [
                  { content: '全部', index: 1 },
                  { content: '部分', index: 2 },
                  { content: '无', index: 3 },
                ],
              },
              {
                header: '列标题2',
                columnId: '1',
                choices: [],
              },
            ],
          },
          title: '点击开始编辑表格题标题',
          componentName: 'custom-table',
          name: '表格题',
          id: 6,
        },
        {
          content: {
            row: [{ link: '' }],
            column: [
              {
                header: '列标题1',
                columnId: '1',
                choices: [
                  { content: '全部', index: 1 },
                  { content: '部分', index: 2 },
                  { content: '无', index: 3 },
                ],
              },
              {
                header: '列标题2',
                columnId: '1',
                choices: [],
              },
            ],
          },
          title: '点击开始编辑引用类型·表格题标题',
          componentName: 'link-table',
          name: '引用类型·表格题',
          id: 10,
        },
        {
          content: [],
          title: '点击开始编辑HTML编程题标题',
          componentName: 'html-coding',
          name: 'HTML编程题',
          id: 7,
        },
        {
          content: [{ value: '' }],
          title: '点击开始编辑JavaScript编程题标题',
          componentName: 'js-coding',
          name: 'JavaScript编程题',
          id: 8,
        },
        {
          content: '点击输入问卷描述文案',
          componentName: 'custom-question',
          name: '问卷基本信息',
          title: '点击开始编辑标题',
          id: -1,
          sections: [{ title: '第一部分', startIndex: 1, endIndex: 2 }],
        },
        {
          content: [{ value: '' }],
          componentName: 'ask',
          name: '问答题',
          id: 9,
        },
      ],
      source: [
        {
          content: '点击输入问卷描述文案',
          componentName: 'custom-question',
          name: '问卷基本信息',
          title: '点击开始编辑标题',
          requireFlag: 1,
          autoIndex: 1,
          sections: [{ title: '', startIndex: 1, endIndex: 1 }],
          id: -1,
        },
      ],
      activeIndex: 0,
      copyData: null,
    }
  },
  computed: {
    linkRowList() {
      const rowList = this.source[this.activeIndex].content.row || []
      const row = deepCopy(rowList[0] || {})
      delete row.link
      return row.shortNameList
    },
    ifRemarkItem() {
      return this.source[this.activeIndex] && ['radio', 'checkbox', 'sort'].includes(this.source[this.activeIndex].componentName)
    },
    remarkItems() {
      const choices = this.source[this.activeIndex].content || []
      return Array.isArray(choices) ? choices.filter(item => item.questionChoiceType).map(item => item.name) : []
    },
    questionId() {
      return this.$route.query.id
    }
  },
  watch: {
    source: {
      handler(newVal) {
        saveSource(newVal, this.activeIndex)
        if (this.source.length > 0 && this.source[0].sections && this.source[0].sections.length > 0) {
          const length = this.source[0].sections.length
          this.source[0].sections[length - 1].endIndex = this.source.length
          // console.log(this.source[0].sections[length - 1])
        }

      },
      deep: true,
    },
  },
  async created() {
    this.list.forEach((item) => {
      if (item.componentName) {
        this.$options.components[item.componentName] = resolve => require([`@/components/lowcode/customComponents/${item.componentName}.vue`], resolve)
      }
    })
    document.addEventListener('keydown', this.handleKeyDown)
    document.addEventListener('keyup', this.handleKeyUp)
    document.addEventListener('paste', this.getClipboard)

    const id = this.$route.query.id
    // console.log('res', id)
    const res = await getById(id)
    // console.log('res', res)
    if (res && res.length > 0) {
      return this.setSource(res)
    }
    const source = getItem('source')
    if (source) {
      this.setSource(source)
    }
  },
  mounted() {
    // console.log(this.$refs.customComponentRef0, this.$options.components)
    this.contextMenuTarget = this.$refs.content
  },
  destroyed() {
    document.removeEventListener('keydown', this.handleKeyDown)
    document.removeEventListener('keyup', this.handleKeyUp)
    document.removeEventListener('paste', this.getClipboard)
  },
  methods: {
    changeChoiceRemark(e) {
      const nameList = e || []

      this.source[this.activeIndex].content.forEach(choice => {
        if (nameList.includes(choice.name)) {
          choice.questionChoiceType = 1
        } else {
          choice.questionChoiceType = 0
        }
      })

      // console.log('changeChoiceRemark', e)
    },
    onLinkRowChange(index, value) {
      this.linkRowList.splice(index, 1, value)
      this.source[this.activeIndex].content.row[0].shortNameList = this.linkRowList
      this.$forceUpdate()
    },
    onLinkRowRemove(index) {
      this.linkRowList.splice(index, 1)
      this.source[this.activeIndex].content.row[0].shortNameList = this.linkRowList
      this.$forceUpdate()
    },
    addLinkRow() {
      this.linkRowList.push('新增的别名')
      this.source[this.activeIndex].content.row[0].shortNameList = this.linkRowList
      this.$forceUpdate()
    },
    setSource(list) {
      if (!list || list.length === 0) return false
      const sortedList = deepCopy(list).sort((a, b) => a.id - b.id)
      let lastId = sortedList[sortedList.length - 1].id
      if (lastId.toString().length > 1000000) lastId = 1
      idGlobal = lastId + 1
      this.source = list
    },
    addSection() {
      this.source[0] &&
        this.source[0].sections.push({
          title: '',
          startIndex: '',
          endIndex: '',
        })
    },
    removeSection(index) {
      this.source[0] && this.source[0].sections.splice(index, 1)
    },
    changeAutoIndex(e) {
      // console.log('change', e)
      const item = this.source[this.activeIndex]
      if (item && item.id >= 0) return false
      this.source.forEach((item) => {
        item.autoIndex = e.target.value
      })
    },
    scroll(index) {
      if (document.querySelector(`#id_${index}`)) {
        // console.log('scroll')
        document.querySelector(`#id_${index}`).scrollIntoView({
          behavior: 'smooth',
          block: 'start',
          inline: 'nearest',
        })
      }
    },
    // 正式环境上线
    async deploy() {
      const res = await deployQuestion(this.source, this.$route.query.id)
      this.$message.success('发布成功，自动打开生产预览页面')
      window.open(res.url)
    },
    // 保存到开发环境
    async save() {
      if (this.$route.query.id) {
        this.result = (await edit(this.source, this.$route.query.id)) || {}
      } else {
        this.result = (await submit(this.source)) || {}
      }

      this.$message.success('保存成功')
      if (!this.$route.query.id) {
        setTimeout(() => {
          window.location.href = `${window.location.href}?id=${this.result.questionnaireId}`
        }, 1000)
      }
    },
    // 预览
    async preview() {
      // console.log('result', JSON.stringify(this.result))
      const id = this.$route.query.id
      if (!this.result || !this.result.url) {
        this.result.url = await preview(id)
      }
      // console.log('url', this.result.url)
      window.open(this.result.url)
    },
    beforeUpload() {
      return false
    },
    async uploadJSON(e) {
      console.info('uploadJSON', e)
      const file = e.file
      const list = await readFile(file)
      if (!this.$route.query.id) {
        list.forEach(item => {
          item.questionId = undefined
          item.sections && item.sections.forEach(section => { section.sectionId = undefined })
          if (Array.isArray(item.content)) {
            item.content.forEach(choice => { choice.questionChoiceId = undefined })
          } else if (typeof (item.content) === 'object') {
            item.content.questionChoiceId = undefined
          }
        })
      }

      this.source = list
      console.info('upload', list)
    },
    exportJSON() {
      // console.log('json', deepCopy(this.source))
      const fileName = `question_${+new Date()}`
      // console.log('fileName', fileName)
      const str = JSON.stringify(this.source, undefined, 2)
      const blob = new Blob([str]) //  创建 blob 对象
      const source = URL.createObjectURL(blob)
      saveFile(source, `${fileName}.json`)
    },
    clear() {
      this.activeIndex = 0
      this.source = this.source.slice(0, 1)
    },
    goback() {
      // this.$router.go(-1)
      this.$router.push('./list')
    },
    undo() {
      const { source, activeIndex } = snapshot.undo()
      this.source = source
      this.activeIndex = activeIndex
    },
    redo() {
      const { source, activeIndex } = snapshot.redo()
      this.source = source
      this.activeIndex = activeIndex
    },

    handleKeyDown(e) {
      if (e.keyCode === ctrlKey) {
        isCtrlDown = true
      } else if (isCtrlDown && e.keyCode === cKey) {
        // Ctrl + c
        // console.log('copy')
        this.copy()
      } else if (isCtrlDown && e.keyCode === vKey) {
        // Ctrl + v
        // console.log('paste')
        // this.paste();
      } else if (isCtrlDown && e.keyCode === xKey) {
        // Ctrl + x
        // console.log('cut')
        this.cut()
      } else if (e.keyCode === dKey) {
        // console.log('delete')
        this.del()
      } else if (isCtrlDown && e.keyCode === zKey) {
        // Ctrl + z
        this.undo()
      } else if (isCtrlDown && e.keyCode === yKey) {
        // Ctrl + y
        this.redo()
      }
    },
    handleKeyUp(e) {
      if (e.keyCode === ctrlKey) {
        isCtrlDown = false
      }
    },
    copy2Clipboard(data) {
      this.copyData = data
      this.$nextTick(() => {
        this.$refs.clipboard.select()
        document.execCommand('copy')
      })
    },
    getClipboard(e) {
      if (e.clipboardData || e.originalEvent) {
        const clipboardData = e.clipboardData || window.clipboardData
        const val = clipboardData.getData('text')
        console.info('系统剪切板内容', val)
        try {
          const data = JSON.parse(val)
          if (data.componentName && data.id && data.content && data.title) {
            this.copyData = data
            this.paste()
            e.preventDefault()
          }
        } catch (e) {
          console.info('粘贴失败')
        }
      }
    },
    copy() {
      const _data = this.source[this.activeIndex]
      if (!_data) return this.$message.error('请先选择组件')
      // this.copyData = _data;
      this.copy2Clipboard(_data)
      this.contextMenuVisible = false
      this.$message.success(`选中第${this.activeIndex + 1}个组件并复制`)
    },
    paste() {
      if (!this.copyData) return this.$message.error('请先选中组件并复制')
      console.info('data', JSON.stringify(this.copyData))
      const _data = deepCopy(this.copyData)
      _data.id = idGlobal++
      _data.questionId = ''
      if (this.activeIndex === -1) {
        this.activeIndex = this.source.length
      }
      this.activeIndex++
      this.source.splice(this.activeIndex, 0, _data)
      this.contextMenuVisible = false
    },
    cut() {
      const _data = this.source[this.activeIndex]
      if (!_data) return this.$message.error('请先选择组件')
      if (this.source.length <= 1) { return this.$message.error('暂不支持清空操作') }
      this.source.splice(this.activeIndex, 1)
      // this.copyData = _data;
      this.copy2Clipboard(_data)
      this.contextMenuVisible = false
      this.$message.success(`选中第${this.activeIndex + 1}个组件并剪切`)
      this.activeIndex = -1
    },
    del() {
      const _data = this.source[this.activeIndex]
      if (!_data) return this.$message.error('请先选择组件')
      if (this.source.length <= 1) { return this.$message.error('暂不支持清空操作') }
      this.source.splice(this.activeIndex, 1)
      this.contextMenuVisible = false
      this.activeIndex--
      this.$message.success('删除成功！')
    },
    choose(item, index) {
      // console.log('选中的组件索引', JSON.stringify(item), index)
      this.activeIndex = index

    },
    complete() {
      // console.log('complete')
    },
    clone({ name, componentName, content, title }) {
      // console.log('clone ', arguments)
      return {
        isShow: 1,
        minChoices: 0,
        maxChoices: 0,
        name,
        requireFlag: 1,
        autoIndex: 1,
        componentName,
        content: deepCopy(content),
        title,
        id: idGlobal++,
      }
    },
    addSource(element) {
      const data = deepCopy(element)
      data.id = idGlobal++
      data.requireFlag = 1
      data.autoIndex = 1
      data.isShow = 1
      data.minChoices = 0
      data.maxChoices = 0
      this.source.push(data)
      this.activeIndex = this.source.length - 1
      this.scroll(this.activeIndex)
    },
    log(evt) {
      window.console.info(evt)
    },
    dragEnd(e) {
      // console.log(e)
      if (e.newIndex === 0) {
        const element = this.source.shift()
        this.source.splice(1, 0, element)
      }
      snapshot.record({ source: this.source, activeIndex: this.activeIndex })
    },
    onMove(e) {
      // if(e.draggedContext)
      // console.log(e.draggedContext)
      if (e.draggedContext.element.id === -1) return false
      if (e.draggedContext.futureIndex === 0) return false
      this.activeIndex = e.draggedContext.futureIndex
      // false表示阻止拖拽
      return true
    },
  },
}
</script>
<style lang="less" scoped>
@import "index.less";
</style>
